#include "xin.h"
#include <QVector2D>
#include <QVector3D>
#include <QDebug>
struct VertexData
{
    QVector3D position;
    QVector2D texCoord;
};
CXin::CXin(GLFuncName *func)
    : m_func(func)
    , m_indexBuf(QOpenGLBuffer::IndexBuffer)
    , m_vertexShader(QOpenGLShader::Vertex)
    , m_fragmentShader(QOpenGLShader::Fragment)
    , m_texture(QOpenGLTexture::Target2D)
{
    initBuffer();
    initShader();
    initTexture();
}
CXin::~CXin()
{
    m_arrayBuf.destroy();
    m_indexBuf.destroy();
    m_texture.destroy();
}
void CXin::initBuffer()
{
    m_arrayBuf.create();
    m_indexBuf.create();

    VertexData vertices[] = {
        // Vertex data for face 0
        {QVector3D(-1.0f/3, -1.0f/3,  3.0f/3), QVector2D(0.0f, 0.0f)},  // v0
        {QVector3D( 1.0f/3, -1.0f/3,  3.0f/3), QVector2D(1.0f, 0.0f)}, // v1
        {QVector3D( 1.0f/3,  1.0f/3,  3.0f/3), QVector2D(0.0f, 1.0f)},  // v2
        {QVector3D(-1.0f/3,  1.0f/3,  3.0f/3), QVector2D(1.0f, 1.0f)}, // v3

        // Vertex data for face 1
        {QVector3D(-3.0f/3, -1.0f/3,  1.0f/3), QVector2D(0.0f, 0.0f)}, // v4
        {QVector3D(-1.0f/3, -3.0f/3,  1.0f/3), QVector2D(1.0f, 0.0f)}, // v5
        {QVector3D( 1.0f/3, -3.0f/3,  1.0f/3), QVector2D(0.0f, 1.0f)},  // v6
        {QVector3D( 3.0f/3, -1.0f/3,  1.0f/3), QVector2D(1.0f, 1.0f)}, // v7
        {QVector3D( 3.0f/3,  1.0f/3,  1.0f/3), QVector2D(0.0f, 0.0f)}, // v8
        {QVector3D( 1.0f/3,  3.0f/3,  1.0f/3), QVector2D(1.0f, 0.0f)},  // v9
        {QVector3D(-1.0f/3,  3.0f/3,  1.0f/3), QVector2D(0.0f, 1.0f)}, // v10
        {QVector3D(-3.0f/3,  1.0f/3,  1.0f/3), QVector2D(1.0f, 1.0f)},  // v11

        {QVector3D(-3.0f/3, -1.0f/3, -1.0f/3), QVector2D(0.0f, 0.0f)}, // v12
        {QVector3D(-1.0f/3, -3.0f/3, -1.0f/3), QVector2D(1.0f, 0.0f)}, // v13
        {QVector3D( 1.0f/3, -3.0f/3, -1.0f/3), QVector2D(0.0f, 1.0f)},  // v14
        {QVector3D( 3.0f/3, -1.0f/3, -1.0f/3), QVector2D(1.0f, 1.0f)}, // v15
        {QVector3D( 3.0f/3,  1.0f/3, -1.0f/3), QVector2D(0.0f, 0.0f)}, // v16
        {QVector3D( 1.0f/3,  3.0f/3, -1.0f/3), QVector2D(1.0f, 0.0f)},  // v17
        {QVector3D(-1.0f/3,  3.0f/3, -1.0f/3), QVector2D(0.0f, 1.0f)}, // v18
        {QVector3D(-3.0f/3,  1.0f/3, -1.0f/3), QVector2D(1.0f, 1.0f)},  // v19

        // Vertex data for face 5
        {QVector3D(-1.0f/3, -1.0f/3, -3.0f/3), QVector2D(0.0f, 0.0f)},  // v20
        {QVector3D( 1.0f/3, -1.0f/3, -3.0f/3), QVector2D(1.0f, 0.0f)}, // v21
        {QVector3D( 1.0f/3,  1.0f/3, -3.0f/3), QVector2D(0.0f, 1.0f)},  // v22
        {QVector3D(-1.0f/3,  1.0f/3, -3.0f/3), QVector2D(1.0f, 1.0f)}, // v23
    };

    GLushort indices[] = {
        0,1,3,1,2,3,
        5,6,0,6,1,0,
        1,7,2,7,8,2,
        2,9,3,9,10,3,
        3,11,0,11,4,0,
        4,12,5,12,13,5,
        5,13,6,13,14,6,
        6,14,7,14,15,7,
        7,15,8,15,16,8,
        8,16,9,16,17,9,
        9,17,10,17,18,10,
        10,18,11,18,19,11,
        11,19,4,19,12,4,
        23,22,20,22,21,20,
        20,21,13,13,21,14,
        21,22,15,15,22,16,
        22,23,17,17,23,18,
        23,20,19,19,20,12,
        4,5,0,
        11,3,10,
        1,6,7,
        2,8,9,
        12,20,13,
        21,15,14,
        22,17,16,
        23,19,18
    };
    m_indexCount = sizeof indices / sizeof(indices[0]);

    m_func->glGenVertexArrays(1, &m_vao);
    m_func->glBindVertexArray(m_vao);

    m_indexBuf.bind();
    m_indexBuf.allocate(indices, sizeof indices);

    m_arrayBuf.bind();
    m_arrayBuf.allocate(vertices, sizeof vertices);

    const int positionLocation = 0;
    m_func->glEnableVertexAttribArray(positionLocation);
    m_func->glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof (VertexData), nullptr);

    const int texCoordLocation =  1;
    m_func->glEnableVertexAttribArray(texCoordLocation);
    m_func->glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void *)(sizeof (QVector3D)));

    m_func->glBindVertexArray(0);
}

void CXin::initShader()
{
    const auto vShader = u8R"(#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;
uniform mat4 mvp;
uniform mat4 mt1;
uniform mat4 mt2;
out vec2 vTexCoord;
void main()
{
    vTexCoord = texCoord;
    gl_Position = mvp * vec4(position, 1.0);
}
)";
    const auto fShader = R"(#version 330 core
uniform sampler2D cubeTexture;
in vec2 vTexCoord;
out vec4 outColor;
void main()
{
    outColor = texture(cubeTexture, vTexCoord);
}
)";
    if (!m_vertexShader.compileSourceCode(vShader)) {
        qWarning() << m_vertexShader.log();
    }
    if (!m_fragmentShader.compileSourceCode(fShader)) {
        qWarning() << m_fragmentShader.log();
    }
}
void CXin::initTexture()
{
    QImage img(":/assets/xin.jpg");
    m_texture.setData(img.mirrored());
    m_texture.setMinificationFilter(QOpenGLTexture::Nearest);
    m_texture.setMagnificationFilter(QOpenGLTexture::Linear);
    m_texture.setWrapMode(QOpenGLTexture::Repeat);
}
void CXin::draw(QOpenGLShaderProgram &program, const QMatrix4x4 &model, const QMatrix4x4 &view, const QMatrix4x4 &project)
{
    program.removeAllShaders();
    program.addShader(&m_vertexShader);
    program.addShader(&m_fragmentShader);
    if (!program.link()) {
        qWarning() << program.log();
        return;
    }
    if (!program.bind()) {
        qWarning() << program.log();
        return;
    }
    program.setUniformValue("mvp", project * view * model);

    m_texture.bind();
    program.setUniformValue("cubeTexture", 0);

    m_func->glBindVertexArray(m_vao);

    m_func->glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_SHORT, nullptr);
}
